/*
 * Copyright (c) 2020 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package me.yokeyword.fragmentation;

import me.yokeyword.fragmentation.extensions.FractionEx;
import me.yokeyword.fragmentation.extensions.FractionManagerEx;
import me.yokeyword.fragmentation.helper.internal.ResultRecord;
import me.yokeyword.fragmentation.helper.internal.VisibleDelegate;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.fraction.Fraction;
import ohos.aafwk.ability.fraction.FractionAbility;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Component;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.utils.PacMap;
import ohos.utils.Sequenceable;

import java.util.Optional;

public class SupportFractionDelegate {
    static final int STATUS_UN_ROOT = 0;
    private EventHandler mHandler;
    private boolean mFirstCreateView = true;
    int mContainerId;

    private TransactionDelegate mTransactionDelegate;
    private VisibleDelegate mVisibleDelegate;
    PacMap mNewBundle;
    PacMap mArgs;

    private ISupportFraction mSupportF;
    private FractionEx mFractionEx;
    private Fraction mFraction;
    private FractionManagerEx mFractionManagerEx;
    protected FractionAbility _mAbility;
    private ISupportAbility mSupport;

    private boolean mRootViewClickable;

    public SupportFractionDelegate(ISupportFraction support) {
        if (!(support instanceof Fraction))
            throw new RuntimeException("Must extends Fraction");
        this.mSupportF = support;
        this.mFraction = (Fraction) support;
    }

    public void setArguments(PacMap arg) {
        mArgs = arg;
    }

    public PacMap getArguments() {
        if (mFractionEx == null) {
            return mArgs;
        } else {
            return mFractionEx.getArguments();
        }
    }

    void setFractionManagerEx(FractionManagerEx fex) {
        mFractionManagerEx = fex;
        mFractionEx = mFractionManagerEx.checkNewRecordOrAdd(mFraction);
    }

    public void onComponentAttached(Ability ability) {
        if (ability instanceof ISupportAbility) {
            this.mSupport = (ISupportAbility) ability;
            this._mAbility = (FractionAbility) ability;
            mFractionManagerEx = ((ISupportAbility) ability).getSupportDelegate().getSupportFractionManagerEx();
            mTransactionDelegate = mSupport.getSupportDelegate().getTransactionDelegate();
            mFractionEx = mFractionManagerEx.checkNewRecordOrAdd(mFraction);
        } else {
            throw new RuntimeException(ability.getClass().getSimpleName() + " must impl ISupportAbility!");
        }
    }

    public void onStart(Intent savedInstanceState) {
        getVisibleDelegate().onCreate(savedInstanceState);
        if (mFractionEx == null)
            return;
        PacMap bundle = mFractionEx.getArguments();
        if (bundle != null) {
            int mRootStatus = bundle.getIntValue(TransactionDelegate.FRAGMENTATION_ARG_ROOT_STATUS, STATUS_UN_ROOT);
            mContainerId = bundle.getIntValue(TransactionDelegate.FRAGMENTATION_ARG_CONTAINER);
            boolean mReplaceMode = bundle.getBooleanValue(TransactionDelegate.FRAGMENTATION_ARG_REPLACE, false);
        }
    }

    public void onSaveInstanceState(PacMap outState) {
        getVisibleDelegate().onSaveInstanceState(outState);
        outState.putBooleanValue(TransactionDelegate.FRAGMENTATION_STATE_SAVE_IS_HIDDEN, mFractionEx.isHide());
        outState.putIntValue(TransactionDelegate.FRAGMENTATION_ARG_CONTAINER, mContainerId);
    }

    public void onAbilityCreated(PacMap savedInstanceState) {
        getVisibleDelegate().onAbilityCreated(savedInstanceState);

        Component component = mFractionEx.getFraction().getComponent();
        if (component != null) {
            mRootViewClickable = component.isClickable();
            component.setClickable(true);
            setBackground(component);
        }

        if (mFirstCreateView) {
            mFirstCreateView = false;
        }
    }

    public void onActive() {
        getVisibleDelegate().onActive();
    }

    public void onInactive() {
        getVisibleDelegate().onInactive();
    }

    public void onDestroyView() {
        mSupport.getSupportDelegate().mFractionClickable = true;
        getVisibleDelegate().onDestroyView();
    }

    public void onDestroy() {
        mTransactionDelegate.handleResultRecord(mFractionEx);
    }

    public void onHiddenChanged(boolean hidden) {
        getVisibleDelegate().onHiddenChanged(hidden);
    }

    public void setUserVisibleHint(boolean isVisibleToUser) {
        getVisibleDelegate().setUserVisibleHint(isVisibleToUser);
    }

    /**
     * Causes the Runnable r to be added to the action queue.
     * <p>
     * The runnable will be run after all the previous action has been run.
     * <p>
     * 前面的事务全部执行后 执行该Action
     * @param runnable runnable task
     *
     * @deprecated Use {@link #post(Runnable)} instead.
     */
    @Deprecated
    public void enqueueAction(Runnable runnable) {
        post(runnable);
    }

    /**
     * Causes the Runnable r to be added to the action queue.
     * <p>
     * The runnable will be run after all the previous action has been run.
     * <p>
     * @param runnable runnable task to execute in foreground
     * 前面的事务全部执行后 执行该Action
     */
    public void post(final Runnable runnable) {
        mTransactionDelegate.post(runnable);
    }

    /**
     * Return true if the fraction has been supportVisible.
     * @return true if visble
     */
    final public boolean isSupportVisible() {
        return getVisibleDelegate().isSupportVisible();
    }

    /**
     * 类似 {@link Ability#setResult(int, Intent)}
     * <p>
     * Similar to {@link Ability#setResult(int, Intent)}
     * @param bundle data
     * @param resultCode action id
     * @see #startForResult(ISupportFraction, int)
     */
    public void setFractionResult(int resultCode, PacMap bundle) {
        PacMap args = mFractionEx.getArguments();
        if (args == null || !args.hasKey(TransactionDelegate.FRAGMENTATION_ARG_RESULT_RECORD)) {
            return;
        }

        Optional<Sequenceable> oresultRecord = args.getSequenceable(TransactionDelegate.FRAGMENTATION_ARG_RESULT_RECORD);
        ResultRecord resultRecord = oresultRecord.isPresent() ? (ResultRecord) (oresultRecord.get()) : null;
        if (resultRecord != null) {
            resultRecord.resultCode = resultCode;
            resultRecord.resultBundle = bundle;
        }
    }


    /**
     * 在start(TargetFraction,LaunchMode)时,启动模式为SingleTask/SingleTop, 回调TargetFraction的该方法
     * 类似 {@link Ability#onNewIntent(Intent)}
     * <p>
     * Similar to {@link Ability#onNewIntent(Intent)}
     *
     * @param args putNewBundle(PacMap newBundle)
     * @see #start(ISupportFraction, int)
     */
    public void onNewBundle(PacMap args) {
    }

    /**
     * 添加NewBundle,用于启动模式为SingleTask/SingleTop时
     * @param newBundle Bundle data to pass
     * @see #start(ISupportFraction, int)
     */
    public void putNewBundle(PacMap newBundle) {
        this.mNewBundle = newBundle;
    }

    /**
     * Back Event
     *
     * @return false则继续向上传递, true则消费掉该事件
     */
    public boolean onBackPressedSupport() {
        onDestroy();
        return false;
    }


    /**
     * 加载根Fraction, 即Ability内的第一个Fraction 或 Fraction内的第一个子Fraction
     * @param toFraction Fraction to load
     * @param containerId resource id
     */
    public void loadRootFraction(int containerId, ISupportFraction toFraction) {
        loadRootFraction(containerId, toFraction, true, false);
    }

    public void loadRootFraction(int containerId, ISupportFraction toFraction, boolean addToBackStack, boolean allowAnim) {
        mTransactionDelegate.loadRootTransaction(mFractionManagerEx, containerId, toFraction, addToBackStack, allowAnim);
    }

    public void start(ISupportFraction toFraction) {
        start(toFraction, ISupportFraction.LAUNCHMODE_STANDARD);
    }

    /** Start new fraction
     * @param launchMode Similar to Ability's LaunchMode.
     * @param toFraction fraction to start
     */
    public void start(final ISupportFraction toFraction, int launchMode) {
        mTransactionDelegate.dispatchStartTransaction(mFractionManagerEx, mSupportF, toFraction, 0, launchMode, TransactionDelegate.TYPE_ADD);
    }

    /**
     * Launch an fraction for which you would like a result when it poped.
     * @param requestCode request code for operation
     * @param toFraction  to fraction
     */
    public void startForResult(ISupportFraction toFraction, int requestCode) {
        mTransactionDelegate.dispatchStartTransaction(mFractionManagerEx, mSupportF, toFraction, requestCode, ISupportFraction.LAUNCHMODE_STANDARD, TransactionDelegate.TYPE_ADD_RESULT);
    }

    /**
     * Start the target Fraction and pop itself
     * @param toFraction start new fraction
     */
    public void startWithPop(ISupportFraction toFraction) {
        mTransactionDelegate.startWithPop(mFractionManagerEx, mSupportF, toFraction);
    }

    public void pop() {
        mTransactionDelegate.pop(mFractionManagerEx);
    }

    /**
     * Pop the last fraction transition from the manager's fraction
     * back stack.
     * <p>
     * 出栈到目标fragment
     *
     * @param targetFractionClass   目标fragment
     * @param includeTargetFraction 是否包含该fragment
     */
    public void popTo(Class<?> targetFractionClass, boolean includeTargetFraction) {
        popTo(targetFractionClass, includeTargetFraction, null);
    }

    /**
     * If you want to begin another FractionTransaction immediately after popTo(), use this method.
     * 如果你想在出栈后, 立刻进行FractionTransaction操作，请使用该方法
     * @param afterPopTransactionRunnable begin another FractionTransaction immediately after popTo
     * @param includeTargetFraction  remove this fraction too
     * @param targetFractionClass  remove the fraction
     */
    public void popTo(Class<?> targetFractionClass, boolean includeTargetFraction, Runnable afterPopTransactionRunnable) {
        popTo(targetFractionClass, includeTargetFraction, afterPopTransactionRunnable);
    }

    public ISupportFraction getTopFraction() {
        return SupportHelper.getTopFraction(mFractionManagerEx);
    }

    public void setBackground(Component view) {
    }

    private EventHandler getHandler() {
        if (mHandler == null) {
            mHandler = new EventHandler(EventRunner.getMainEventRunner());
        }
        return mHandler;
    }

    public VisibleDelegate getVisibleDelegate() {
        if (mVisibleDelegate == null) {
            mVisibleDelegate = new VisibleDelegate(mSupportF);
        }
        return mVisibleDelegate;
    }

    public FractionAbility getAbility() {
        return _mAbility;
    }
}